package com.bsj.travel.device.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.bsj.travel.cached.redis.RedisCached;
import com.bsj.travel.config.rocketmq.producer.BaseMqPushUtil;
import com.bsj.travel.constant.*;
import com.bsj.travel.constants.RedisConstant;
import com.bsj.travel.def.common.DO.DeviceDO;
import com.bsj.travel.def.common.DO.ProductCategoryDO;
import com.bsj.travel.def.common.DO.ProductDO;
import com.bsj.travel.def.common.DO.ProductTopicDO;
import com.bsj.travel.def.common.DTO.mqtt.MqttEvent;
import com.bsj.travel.def.common.DTO.mqtt.MqttEventPublish;
import com.bsj.travel.def.common.DTO.web.device.DeviceDTO;
import com.bsj.travel.def.common.DTO.web.device.DeviceParamDTO;
import com.bsj.travel.def.common.DTO.web.device.DeviceRemarkDTO;
import com.bsj.travel.def.common.DTO.web.device.DeviceSearchDTO;
import com.bsj.travel.def.common.DTO.web.productTopic.ProductTopicSearchDTO;
import com.bsj.travel.def.common.VO.ProductTopicVO;
import com.bsj.travel.def.common.VO.QuantityCountVO;
import com.bsj.travel.def.common.VO.device.*;
import com.bsj.travel.def.global.JsonResult;
import com.bsj.travel.def.global.JsonResultEnum;
import com.bsj.travel.def.global.mq.Rp;
import com.bsj.travel.def.mqtt.auth.MqttSignature;
import com.bsj.travel.device.service.DeviceService;
import com.bsj.travel.device.service.ProductService;
import com.bsj.travel.device.service.ProductTopicService;
import com.bsj.travel.exception.ApiException;
import com.bsj.travel.mapper.mysql.CategoryMapper;
import com.bsj.travel.mapper.mysql.DeviceMapper;
import com.bsj.travel.mapper.mysql.ProductCategoryMapper;
import com.bsj.travel.mapper.mysql.ProductMapper;
import com.bsj.travel.security.auth.AuthProduct;
import com.bsj.travel.util.DataUtil;
import com.bsj.travel.util.DateUtils;
import com.bsj.travel.util.encipher.DeviceEncryptUtil;
import com.bsj.travel.util.excel.ModelExcelListener;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.time.Instant;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

/***
 * @description DeviceServiceImpl
 * @author LiJiaXin
 * @version 1.0.0
 * @time 2024/1/15 14:41
 **/
@Slf4j
@Service
public class DeviceServiceImpl implements DeviceService {

    @Resource
    private DeviceMapper deviceMapper;

    @Resource
    private ProductMapper productMapper;

    @Resource
    private CategoryMapper categoryMapper;

    @Resource
    private ProductCategoryMapper productCategoryMapper;

    @Autowired
    private RedisCached redisCached;

    @Resource
    private AuthProduct authProduct;

    @Autowired
    private ProductService productService;

    @Autowired
    private ProductTopicService productTopicService;

    @Autowired
    private BaseMqPushUtil baseMqPushUtil;

    @Value("${mqttSignature.ip}")
    private String ip;

    @Value("${mqttSignature.port}")
    private String port;

    /**
     * 添加设备
     *
     * @param deviceDTO
     * @return com.bsj.travel.def.global.JsonResult
     * @author ljx
     * @time 2024/1/15 14:49
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public JsonResult addDevice(DeviceDTO deviceDTO) {
        paramCheck(deviceDTO);
        DeviceDO deviceDO = new DeviceDO();
        BeanUtils.copyProperties(deviceDTO, deviceDO);
        String productKey = deviceDTO.getProductKey();
        deviceDO.setTableName(Constants.DEVICE_TAB_STR + productKey);
        deviceDO.setDeviceSecret(DeviceEncryptUtil.EncryptDevSecret(productKey, deviceDTO.getDeviceName()));
        deviceDO.setTerminalModel(TerminalModelConstants.DEVICE_DIRECT_CONNECTION);
        if (deviceMapper.insert(deviceDO) > 0) {
            redisCached.hsetDeviceInfo(productKey, deviceDO);
            sent(deviceDO, productKey);
            return JsonResult.success();
        } else {
            return JsonResult.fail();
        }
    }

    /**
     * 删除设备
     *
     * @param deviceParamDTO
     * @return com.bsj.travel.def.global.JsonResult
     * @author ljx
     * @time 2024/1/15 14:49
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public JsonResult deleteDevice(DeviceParamDTO deviceParamDTO) {
        String productKey = deviceParamDTO.getProductKey();
        authProduct.permissionCheck(productKey);
        List<Long> deviceIds = deviceParamDTO.getDeviceIds();
        if (CollectionUtils.isEmpty(deviceIds)) {
            return JsonResult.fail(JsonResultEnum.PARAM_ERROR);
        }
        String tableName = Constants.DEVICE_TAB_STR + productKey;
        List<String> deviceNames = deviceMapper.selectDeviceNameByDeviceIds(tableName, deviceIds);
        if (CollectionUtils.isEmpty(deviceNames)) {
            throw new ApiException(JsonResultEnum.DEVICE_IS_NOT_EXIST);
        }
        deviceMapper.delete(tableName, deviceIds);
        deviceNames.forEach(deviceName -> {
            DeviceDO deviceDO = redisCached.hgetDeviceInfo(productKey, deviceName);
            if (deviceDO != null) {
                redisCached.hdelDeviceInfo(productKey, deviceName);
                sent(deviceDO, productKey);
            }
        });
        return JsonResult.success();
    }

    /**
     * 修改设备
     *
     * @param deviceDTO
     * @return com.bsj.travel.def.global.JsonResult
     * @author ljx
     * @time 2024/1/15 14:49
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public JsonResult updateDevice(DeviceDTO deviceDTO) {
        Long deviceId = deviceDTO.getDeviceId();
        if (deviceId == null) {
            return JsonResult.fail(JsonResultEnum.PARAM_ERROR);
        }
        String productKey = deviceDTO.getProductKey();
        String tableName = Constants.DEVICE_TAB_STR + productKey;
        paramCheck(deviceDTO);
        DeviceDO oldDeviceDO = deviceMapper.selectById(tableName, deviceId);
        if (oldDeviceDO == null) {
           return JsonResult.fail(JsonResultEnum.DEVICE_IS_NOT_EXIST);
        }
        DeviceDO deviceDO = new DeviceDO();
        BeanUtils.copyProperties(deviceDTO, deviceDO);
        deviceDO.setTableName(tableName);
        if (deviceMapper.updateById(deviceDO) > 0) {
            deviceDO.setDeviceSecret(oldDeviceDO.getDeviceSecret());
            deviceDO.setGmtActive(oldDeviceDO.getGmtActive());
            deviceDO.setGmtCreate(oldDeviceDO.getGmtCreate());
            deviceDO.setIsDisable(oldDeviceDO.getIsDisable());
            //设备号可能会修改
            redisCached.hdelDeviceInfo(productKey, oldDeviceDO.getDeviceName());
            redisCached.hsetDeviceInfo(productKey, deviceDO);
            sent(deviceDO, productKey);
            return JsonResult.success();
        } else {
            return JsonResult.fail();
        }
    }

    /**
     * 根据设备id获取设备信息
     *
     * @param deviceParamDTO
     * @return com.bsj.travel.def.common.VO.device.DeviceVO
     * @author ljx
     * @time 2024/1/15 14:49
     */
    @Override
    public DeviceCorrelationInfoVO getDeviceInfoById(DeviceParamDTO deviceParamDTO) {
        Long deviceId = deviceParamDTO.getDeviceId();
        if (deviceId == null) {
            throw new ApiException(JsonResultEnum.PARAM_ERROR);
        }
        String productKey = deviceParamDTO.getProductKey();
        authProduct.permissionCheck(productKey);
        String tableName = Constants.DEVICE_TAB_STR + productKey;
        DeviceDO deviceDO = deviceMapper.selectById(tableName, deviceId);
        if (deviceDO == null) {
            throw new ApiException(JsonResultEnum.DEVICE_IS_NOT_EXIST);
        }
        Date gmtActive = deviceDO.getGmtActive();
        String deviceName = deviceDO.getDeviceName();
        ProductDO productDO = redisCached.hgetProductInfo(productKey);
        Long productId = productDO.getProductId();

        DeviceCorrelationInfoVO deviceCorrelationInfoVO = new DeviceCorrelationInfoVO();
        BeanUtils.copyProperties(deviceDO, deviceCorrelationInfoVO);
        deviceCorrelationInfoVO.setProductName(productDO.getProductName());
        deviceCorrelationInfoVO.setProductKey(productKey);
        deviceCorrelationInfoVO.setNodesType(productDO.getNodesType());
        deviceCorrelationInfoVO.setAuthMethod(productDO.getAuthMethod());
        deviceCorrelationInfoVO.setGmtCreate(DateUtils.parseDateToStr(deviceDO.getGmtCreate()));
        if (gmtActive == null) {
            deviceCorrelationInfoVO.setStatus(NumericalConstants.ONE);
        } else {
            // 设备状态：设备状态 1：未激活 2：在线 3：离线
            deviceCorrelationInfoVO.setStatus(redisCached.getDeviceStatus(deviceName + "&" + productKey));
            // 设备激活时间
            deviceCorrelationInfoVO.setGmtActive(DateUtils.parseDateToStr(gmtActive));
        }
        DeviceDO deviceCache = redisCached.hgetDeviceInfo(productKey, deviceName);
        if (deviceCache != null && deviceCache.getGmtOnline() != null) {
            deviceCorrelationInfoVO.setGmtOnline(DateUtils.parseDateToStr(new Date(Long.parseLong(deviceCache.getGmtOnline()))));
        }
        if (deviceCache != null && deviceCache.getIpAddress() != null) {
            deviceCorrelationInfoVO.setIp(deviceCache.getIpAddress());
        }
        //MQTT连接参数
        MqttSignature mqttSignature = new MqttSignature(productKey, deviceName, deviceDO.getDeviceSecret(), String.valueOf(System.currentTimeMillis()));
        mqttSignature.setIp(ip);
        mqttSignature.setPort(port);
        deviceCorrelationInfoVO.setMqttSignature(mqttSignature);
        //物模型数据（品类）
        if (productDO.getCategory().equals(NumericalConstants.TWO)) {
            ProductCategoryDO productCategoryDO = productCategoryMapper.selectOne(Wrappers.<ProductCategoryDO>lambdaQuery()
                    .eq(ProductCategoryDO::getProductId, productId));
            deviceCorrelationInfoVO.setCategoryDO(categoryMapper.selectById(productCategoryDO.getCategoryId()));
        }
        //Topic列表
        ProductTopicSearchDTO productTopicSearchDTO = new ProductTopicSearchDTO();
        productTopicSearchDTO.setProductId(productId);
        List<ProductTopicVO> productTopicVOS = productTopicService.listProductTopic(productTopicSearchDTO);
        deviceCorrelationInfoVO.setProductTopicVOS(productTopicVOS);
        return deviceCorrelationInfoVO;
    }

    /**
     * 设备分页
     *
     * @param deviceSearchDTO
     * @return com.bsj.travel.def.global.JsonResult<java.util.List < com.bsj.travel.def.common.VO.device.DeviceVO>>
     * @author ljx
     * @time 2024/1/15 14:50
     */
    @Override
    public JsonResult<List<DeviceVO>> pageDevice(DeviceSearchDTO deviceSearchDTO) {
        List<DeviceVO> deviceVOS = new LinkedList<>();
        String productKey = deviceSearchDTO.getProductKey();
        authProduct.permissionCheck(productKey);
        deviceSearchDTO.setProductKey(Constants.DEVICE_TAB_STR + productKey);
        deviceSearchDTO.setSearchType(NumericalConstants.ONE);
        int total = deviceMapper.countByCondition(deviceSearchDTO);
        if (total == 0) {
            return JsonResult.success(deviceVOS, total);
        }
        String productName = null;
        String nodesType = null;
        ProductDO productDO = redisCached.hgetProductInfo(productKey);
        if (productDO != null) {
             productName = productDO.getProductName();
             nodesType = productDO.getNodesType();
        }
        deviceSearchDTO.initPageNumber();
        deviceVOS = deviceMapper.selectDevicePage(deviceSearchDTO);
        for (DeviceVO deviceVO : deviceVOS) {
            String deviceName = deviceVO.getDeviceName();
            deviceVO.setProductKey(productKey);
            deviceVO.setProductName(productName);
            deviceVO.setNodesType(nodesType);
            if (StringUtils.isBlank(deviceVO.getGmtActive())) {
                deviceVO.setStatus(NumericalConstants.ONE);
            } else {
                deviceVO.setStatus(redisCached.getDeviceStatus(deviceName + "&" + productKey));
            }
            DeviceDO deviceDO = redisCached.hgetDeviceInfo(productKey, deviceName);
            if (deviceDO != null && deviceDO.getGmtOnline() != null) {
                deviceVO.setGmtOnline(DateUtils.parseDateToStr(new Date(Long.parseLong(deviceDO.getGmtOnline()))));
            }

        }
        return JsonResult.success(deviceVOS, total);
    }

    /**
     * 是否禁用
     *
     * @param deviceParamDTO
     * @return com.bsj.travel.def.global.JsonResult
     * @author ljx
     * @time 2024/1/15 14:50
     */
    @Override
    public JsonResult whetherToDisable(DeviceParamDTO deviceParamDTO) {
        Long deviceId = deviceParamDTO.getDeviceId();
        if (deviceId == null) {
            return JsonResult.fail(JsonResultEnum.PARAM_ERROR);
        }
        Integer isDisable = deviceParamDTO.getIsDisable();
        if (isDisable == null || (!isDisable.equals(NumericalConstants.ONE) && !isDisable.equals(NumericalConstants.TWO))) {
            return JsonResult.fail(JsonResultEnum.PARAM_ERROR);
        }
        String productKey = deviceParamDTO.getProductKey();
        authProduct.permissionCheck(productKey);
        String tableName = Constants.DEVICE_TAB_STR + productKey;
        DeviceDO deviceDO = deviceMapper.selectById(tableName, deviceId);
        if (deviceDO == null) {
            return JsonResult.fail(JsonResultEnum.DEVICE_IS_NOT_EXIST);
        }
        deviceParamDTO.setProductKey(tableName);
        if (deviceMapper.whetherToDisable(deviceParamDTO) > 0) {
            deviceDO.setIsDisable(isDisable);
            redisCached.hsetDeviceInfo(productKey, deviceDO);
            sent(deviceDO, productKey);
            return JsonResult.success();
        } else {
            return JsonResult.fail();
        }
    }

    /**
     * 批量添加设备
     *
     * @param deviceParamDTO
     * @return com.bsj.travel.def.global.JsonResult
     * @author ljx
     * @time 2024/1/15 14:51
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public JsonResult batchAddDevice(DeviceParamDTO deviceParamDTO) {
        MultipartFile file = deviceParamDTO.getFile();
        if (file == null || file.getSize() <= 0) {
            return JsonResult.fail(JsonResultEnum.FILE_IS_NULL);
        }
        String filename = file.getOriginalFilename();
        String fileType = filename.substring(filename.lastIndexOf("."));
        if (!Constants.EXCEL_FILE_SUFFIX.equals(fileType)) {
            return JsonResult.fail(JsonResultEnum.FILE_FORMAT_ERROR);
        }
        String productKey = deviceParamDTO.getProductKey();
        authProduct.permissionCheck(productKey);
        //解析文件获取数据
        InputStream inputStream = null;
        try {
            inputStream = file.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        ModelExcelListener listener = new ModelExcelListener();
        EasyExcel.read(inputStream, DeviceTemplateVO.class, listener).sheet().doRead();
        Map<Integer, DeviceTemplateVO> data = listener.getData();
        if (CollectionUtils.isEmpty(data)) {
            return JsonResult.fail(JsonResultEnum.FILE_IS_NULL);
        }
        if (data.size() > NumericalConstants.TEN_THOUSAND) {
            return JsonResult.fail(JsonResultEnum.FILE_MODEL_BIG);
        }
        //校验文件数据
        String tableName = Constants.DEVICE_TAB_STR + productKey;
        String errorInfo = checkImportData(tableName, data);
        if (StringUtils.isNotBlank(errorInfo)) {
            return JsonResult.fail(errorInfo);
        }
        List<DeviceDO> deviceDOS = data.entrySet().stream().map(value -> {
            DeviceTemplateVO deviceTemplateVO = value.getValue();
            DeviceDO deviceDO = new DeviceDO();
            BeanUtils.copyProperties(deviceTemplateVO, deviceDO);
            deviceDO.setTerminalModel(TerminalModelConstants.DEVICE_DIRECT_CONNECTION);
            deviceDO.setDeviceSecret(DeviceEncryptUtil.EncryptDevSecret(productKey, deviceTemplateVO.getDeviceName()));
            return deviceDO;
        }).collect(Collectors.toList());
        deviceMapper.batchAddDevice(tableName, deviceDOS);
        deviceDOS.forEach(deviceDO -> {
            redisCached.hsetDeviceInfo(productKey, deviceDO);
            sent(deviceDO, productKey);
        });
        return JsonResult.success();
    }

    /**
     * 获取设备导出数据
     *
     * @param deviceSearchDTO
     * @return java.util.List<com.bsj.travel.def.common.VO.device.DeviceExcelVO>
     * @author ljx
     * @time 2024/1/17 19:06
     */
    @Override
    public List<DeviceExcelVO> getDeviceReportData(DeviceSearchDTO deviceSearchDTO) {
        String productKey = deviceSearchDTO.getProductKey();
        authProduct.permissionCheck(productKey);
        deviceSearchDTO.setProductKey(Constants.DEVICE_TAB_STR + productKey);
        deviceSearchDTO.setSearchType(NumericalConstants.TWO);
        List<DeviceVO> deviceVOS = deviceMapper.selectDevicePage(deviceSearchDTO);
        List<DeviceExcelVO> deviceExcelVOS = new LinkedList<>();
        if (CollectionUtils.isEmpty(deviceVOS)) {
            return deviceExcelVOS;
        }
        ProductDO productDO = redisCached.hgetProductInfo(productKey);
        String productName = productDO.getProductName();
        String nodesType = productDO.getNodesType();
        deviceExcelVOS = deviceVOS.stream().map(deviceVO -> {
            DeviceExcelVO deviceExcelVO = new DeviceExcelVO();
            BeanUtils.copyProperties(deviceVO, deviceExcelVO);
            deviceExcelVO.setIsDisable(deviceVO.getIsDisable() == 1 ? "是" : "否");
            deviceExcelVO.setProductKey(productKey);
            deviceExcelVO.setProductName(productName);
            deviceExcelVO.setNodesType(nodesTypeTransform(nodesType));
            if (StringUtils.isBlank(deviceVO.getGmtActive())) {
                deviceExcelVO.setStatus("未激活");
            } else {
                int status = redisCached.getDeviceStatus(deviceVO.getDeviceName() + "&" + productKey);
                if (status == NumericalConstants.TWO) {
                    deviceExcelVO.setStatus("在线");
                } else {
                    deviceExcelVO.setStatus("离线");
                }
            }
            return deviceExcelVO;
        }).collect(Collectors.toList());
        return deviceExcelVOS;
    }

    /**
     * 节点类型转换
     *
     * @param nodesType
     * @return java.lang.String
     * @author ljx
     * @time 2024/3/20 17:32
     */
    private String nodesTypeTransform(String nodesType) {
        String result = "";
        switch (nodesType) {
            case "D":
                result = "直连设备";
            case "G":
                result = "网关设备";
            case "S":
                result = "网关子设备";
        }
        return result;
    }

    /**
     * 数量统计
     *
     * @param
     * @return java.util.Map<java.lang.String, java.lang.Object>
     * @author ljx
     * @time 2024/2/18 15:07
     */
    @Override
    public Map<String, Object> quantityCount() {
        //获取当前用户下所有产品
        List<ProductDO> productDOS = productService.selectProductList(NumericalConstants.TWO);
        if (CollectionUtils.isEmpty(productDOS)) {
            return null;
        }
        long totalNum = 0L;
        long activateNum = 0L;
        long onLineNum = 0L;
        List<QuantityCountVO> totalGroup = new LinkedList<>();
        List<QuantityCountVO> activateGroup = new LinkedList<>();
        List<QuantityCountVO> onLineGroup = new LinkedList<>();
        // 当前分钟的时间戳(整分钟),在线数会存在延时，因为每1分钟一次刷新
        String timestamp = Instant.now().atZone(ZoneId.systemDefault()).withSecond(0).withNano(0).toInstant().toEpochMilli() + "";
        for (ProductDO productDO : productDOS) {
            String productKey = productDO.getProductKey();
            String productName = productDO.getProductName();
            List<DeviceDO> deviceDOS = redisCached.hgetAllDeviceInfo(productKey);
            if (CollectionUtils.isEmpty(deviceDOS)) {
                continue;
            }
            long onLine = 0L;
            // 获取产品对应的设备数量
            String onLineCount = redisCached.hget(RedisConstant.CLIENT_CONNECT_SUM_KEY + productKey, timestamp);
            if (onLineCount != null) {
                onLine = Long.parseLong(onLineCount);
            }
            long total = deviceDOS.size();
            long activate = deviceDOS.stream().filter(deviceDO -> deviceDO.getGmtActive() != null).count();
            totalGroup.add(new QuantityCountVO(productName, total));
            activateGroup.add(new QuantityCountVO(productName, activate));
            onLineGroup.add(new QuantityCountVO(productName, onLine));
            totalNum += total;
            activateNum += activate;
        }
        // 实时统计在线数
        Map<String, String> map = redisCached.hgetAll(RedisConstant.SERVER_NODES);
        if (CollectionUtil.isNotEmpty(map)) {
            Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, String> entry = iterator.next();
                String key = entry.getKey();
                long size = redisCached.hlen(RedisConstant.CONNECT_STATUS + key);
                onLineNum += size;
            }
        }
        return Map.of("totalNum", totalNum, "activateNum", activateNum, "onLineNum", onLineNum, "totalGroup", totalGroup, "activateGroup", activateGroup, "onLineGroup", onLineGroup);
    }

    /**
     * 模糊搜索设备号
     *
     * @param productKey
     * @param value
     * @return java.util.List<java.lang.String>
     * @author ljx
     * @time 2024/3/20 19:16
     */

    @Override
    public List<String> fuzzySearchDeviceName(String productKey, String value) {
        if (StringUtils.isAnyBlank(productKey, value)) {
            throw new ApiException(JsonResultEnum.PARAM_ERROR);
        }
        authProduct.permissionCheck(productKey);
        return deviceMapper.fuzzySearchDeviceName(Constants.DEVICE_TAB_STR + productKey, DataUtil.escape(value));
    }

    /**
     * 获取MQTT连接参数
     *
     * @param productKey
     * @param deviceName
     * @return com.bsj.travel.def.mqtt.auth.MqttSignature
     * @author ljx
     * @time 2024/3/22 14:40
     */
    @Override
    public MqttSignature getMqttSignature(String productKey, String deviceName) {
        if (StringUtils.isAnyBlank(productKey, deviceName)) {
            throw new ApiException(JsonResultEnum.PARAM_ERROR);
        }
        authProduct.permissionCheck(productKey);
        DeviceDO deviceDO = redisCached.hgetDeviceInfo(productKey, deviceName);
        if (deviceDO == null) {
            log.info("设备缓存信息为空");
            return null;
        }
        MqttSignature mqttSignature = new MqttSignature(productKey, deviceName, deviceDO.getDeviceSecret(),
                String.valueOf(System.currentTimeMillis()));
        mqttSignature.setIp(ip);
        mqttSignature.setPort(port);
        return mqttSignature;
    }

    /**
     * 校验导入数据
     *
     * @param tableName
     * @param data
     * @return java.lang.String
     * @author ljx
     * @time 2024/1/16 15:28
     */
    private String checkImportData(String tableName, Map<Integer, DeviceTemplateVO> data) {
        Map<String, String> deviceNameMap = new HashMap<>();
        List<String> deviceNames = data.values().stream().map(DeviceTemplateVO::getDeviceName).collect(Collectors.toList());
        List<String> deviceNameList = deviceMapper.selectDeviceNameByDeviceNames(tableName, deviceNames);
        StringBuilder errorInfo = new StringBuilder();
        data.forEach((row, deviceTemplateVO) -> {
            String deviceName = deviceTemplateVO.getDeviceName();
            if (StringUtils.isBlank(deviceName)) {
                errorInfo.append("第" + row + "行设备号不能为空;");
            } else {
                if (!deviceName.matches(RegularExpressionConstants.NUMBERS_AND_LETTERS)) {
                    errorInfo.append("第" + row + "行设备号格式错误;");
                }
                if (deviceNameList.contains(deviceName)) {
                    errorInfo.append("第" + row + "行设备号已存在;");
                }
                if (deviceNameMap.get(deviceName) != null) {
                    errorInfo.append("第" + row + "行设备号在当前文件已存在;");
                }
                deviceNameMap.put(deviceName, deviceName);
            }
        });
        return errorInfo.toString();
    }

    /**
     * 发送消息
     *
     * @param deviceDO
     * @return void
     * @author ljx
     * @time 2024/3/1 10:21
     */
    private void sent(DeviceDO deviceDO, String productKey) {
        Rp<DeviceInfoVO> rp = new Rp<>();
        rp.setTag(BaseServerTopicConstants.DEVICE_LIFE_CYCLE_CHANGE_TAG);
        DeviceInfoVO deviceInfoVO = new DeviceInfoVO();
        BeanUtils.copyProperties(deviceDO, deviceInfoVO);
        rp.setData(deviceInfoVO);
        rp.setProductKey(productKey);
        baseMqPushUtil.sendTopicBytes(BaseServerTopicConstants.PRODUCT_SUBSCRIBE_TOPIC, BaseServerTopicConstants.DEVICE_LIFE_CYCLE_CHANGE_TAG, JSON.toJSONBytes(rp));
    }

    /**
     * 参数校验
     *
     * @param deviceDTO
     * @return void
     * @author ljx
     * @time 2024/1/15 15:55
     */
    private void paramCheck(DeviceDTO deviceDTO) {
        String productKey = deviceDTO.getProductKey();
        authProduct.permissionCheck(productKey);
        //设备号格式校验
        String deviceName = deviceDTO.getDeviceName();
        if (!deviceName.matches(RegularExpressionConstants.NUMBERS_AND_LETTERS)) {
            throw new ApiException(JsonResultEnum.DEVICE_NAME_FORMAT_ERROR);
        }
        //去重校验
        DeviceSearchDTO deviceSearchDTO = new DeviceSearchDTO();
        deviceSearchDTO.setProductKey(Constants.DEVICE_TAB_STR + productKey);
        deviceSearchDTO.setDeviceName(deviceName);
        deviceSearchDTO.setDeviceId(deviceDTO.getDeviceId());
        if (deviceMapper.countByCondition(deviceSearchDTO) > 0) {
            throw new ApiException(JsonResultEnum.DEVICE_NAME_IS_NOT_NULL);
        }
    }

    @Override
    public List<MqttSignatureExcelVO> getMqttSignatureReportData(String productKey) {
        List<DeviceDO> deviceDOS = redisCached.hgetAllDeviceInfo(productKey);
        log.info("数据长度{}", deviceDOS.size());
        return deviceDOS.stream().map(deviceDO -> {
            MqttSignature mqttSignature = new MqttSignature(productKey, deviceDO.getDeviceName(), deviceDO.getDeviceSecret(),
                    String.valueOf(System.currentTimeMillis()));
            MqttSignatureExcelVO mqttSignatureExcelVO = new MqttSignatureExcelVO();
            BeanUtils.copyProperties(mqttSignature, mqttSignatureExcelVO);
            return mqttSignatureExcelVO;
        }).collect(Collectors.toList());
    }

    /**
     * 根据主题发布消息
     * @param productKey 产品key
     * @param deviceName 设备号
     * @param topic 主题信息 /${productKey}/${deviceName}/user/getInfo
     * @param data 发布内容
     * @return
     */
    @Override
    public JsonResult pubMqttMsg(String productKey, String deviceName,
                                 String topic, String data, Integer qos) {
        authProduct.permissionCheck(productKey);
        DeviceDO deviceDO = redisCached.hgetDeviceInfo(productKey, deviceName);
        if (deviceDO == null) {
            throw new ApiException(JsonResultEnum.DEVICE_IS_NOT_EXIST);
        }
        List<ProductTopicDO> topicAllList = productTopicService.getAllList(productKey);
        Map<String, ProductTopicDO> topicMap = topicAllList.stream()
                .collect(Collectors.toMap(ProductTopicDO::getTopicClass, dto -> dto, (existing, replacement) -> existing));
        String topicProduct = com.bsj.travel.util.StringUtils.replaceProduct(topic, productKey);
        ProductTopicDO topicDO = topicMap.get(topicProduct);
        if (topicDO == null) {
            throw new ApiException(JsonResultEnum.TOPIC_IS_NOT_EXIST);
        }
        if (topicDO.getPurview() == 2) {
            throw new ApiException(JsonResultEnum.TOPIC_CLASS_NOT_PUB);
        }
        String topicClass = com.bsj.travel.util.StringUtils.replaceTopicClass(topic, productKey, deviceName);
        MqttEventPublish mqttEventPublish = new MqttEventPublish();
        mqttEventPublish.setTopic(topicClass);
        mqttEventPublish.setData(data);
        mqttEventPublish.setQos(qos);
        String username = deviceName + "&" + productKey;
        mqttEventPublish.setClient(username);
        baseMqPushUtil.sendMqttMsgDownTag(mqttEventPublish);
        return JsonResult.success(mqttEventPublish);
    }

    @Override
    public JsonResult updateRemark(DeviceRemarkDTO deviceRemarkDTO) {
        Long deviceId = deviceRemarkDTO.getDeviceId();
        if (deviceId == null) {
            return JsonResult.fail(JsonResultEnum.PARAM_ERROR);
        }
        String productKey = deviceRemarkDTO.getProductKey();
        String tableName = Constants.DEVICE_TAB_STR + productKey;
        DeviceDO deviceDO = deviceMapper.selectById(tableName, deviceId);
        if (deviceDO == null) {
            return JsonResult.fail(JsonResultEnum.DEVICE_IS_NOT_EXIST);
        }
        deviceDO.setRemark(deviceRemarkDTO.getRemark());
        deviceDO.setTableName(tableName);
        if (deviceMapper.updateRemark(deviceDO) > 0) {
            return JsonResult.success();
        } else {
            return JsonResult.fail();
        }
    }

}
